object ExceptionEx {
  def main() : Unit = {
    try {
      try {
        println(new ArrayUse().useUninitializedArray());
      } catch (e : UninitializedException) {
        println("ERROR " + e.getMessage());
      }
      
	  try{
		  println(new ArrayUse().useOutOfBoundArray());
	  }finally{
		  println("spongebob");
	  }
    } catch (e : OutOfBoundException) { 
      println("ERROR " + e.getMessage());
      
      try {
        println(new ArrayUse().useNegativeValueArray());
      } catch (e2 : ToolException) {
        println("ERROR " + e2.getMessage());
      }
    } finally {
        println("END OF EXCEPTIONS DEMO");
    }
  }
}

class ArrayUse {
  var array : PositiveArray;
  var bin : Int;
  
  def useUninitializedArray() : Int throws UninitializedException,
                                           OutOfBoundException = {
    array = new PositiveArray();
    bin = array.init(5);
    
    // Raise an UninitializedException
    return array.get(2);
  }
  
  def useOutOfBoundArray() : Int throws NegativeValueException,
                                        OutOfBoundException = {
    array = new PositiveArray();
    bin = array.init(5);
    
    // Raise an OutOfBoundException
    return array.set(5,5);
  }
  
  def useNegativeValueArray() : Int throws NegativeValueException,
                                           OutOfBoundException = {
    array = new PositiveArray();
    bin = array.init(5);
    
    // Raise an NegativeValueException
    return array.set(0,0-2);
  }
}

class PositiveArray {
  var outOfBoundException : OutOfBoundException;
  var array : Int[];
  var size : Int;
  var nonInitValue : Int;
  
  /* Create an non-initialized array of size s*/
  def init(s : Int) : Int = {
    var i : Int;
    
    size = s;
    array = new Int[size];
    nonInitValue = 0-1;
    
    i = 0;
    while (i < size) {
      array[i] = nonInitValue;
      i = i+1;
    }
    
    outOfBoundException = new OutOfBoundException();
    return outOfBoundException.setMaxBound(size);
  }
  
  /* Set at index i the given value */
  def set(i : Int,value : Int) : Int throws ToolException = { /*throws OutOfBoundException,
                                            NegativeValueException = {*/
    if (i < size) {
      if (value < 0)
        throw new NegativeValueException().setMessage("Value must be strictly positive");
      
      array[i] = value;
    } else {
      throw outOfBoundException.setAccessingIndex(i);
    }
    
    return array[i];
  }
  
  /* Return the value at the given index i */
  def get(i : Int) : Int throws ToolException = {
    if (i < size) {
      if (array[i] < 0)
        throw new UninitializedException().setMessage("Value at index " + i + " was not initialized");
    } else {
      throw outOfBoundException.setAccessingIndex(i);
    }
    
    return array[i];
  }
}

class OutOfBoundException extends ToolException {
  var maxIndex : Int;
  
  def setMaxBound(max : Int) : Int = {
    maxIndex = max-1;
    return max;
  }
  
  def setAccessingIndex(i : Int) : OutOfBoundException = {
    msg = "Index " + i + " is out of bound";
    
    return this;
  }
  
  def getMessage() : String = {
    return msg + " (max index " + maxIndex + ")";
  }
}

class UninitializedException extends ToolException {
}

class NegativeValueException extends ToolException {
}
